package com.ruben.simplescaffold.utils;

import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.support.*;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 函数式相关工具类
 *
 * @author <achao1441470436@gmail.com>
 * @since 2021/5/4 0004 17:21
 */
public class FunctionUtils {
    private FunctionUtils() {
    }

    /**
     * SerializedLambda 反序列化缓存
     */
    private static final Map<String, WeakReference<LambdaMeta>> FUNC_CACHE = new ConcurrentHashMap<>();

    /**
     * 通过反序列化转换 lambda 表达式，该方法只能序列化 lambda 表达式，不能序列化接口实现或者正常非 lambda 写法的对象
     *
     * @param lambda lambda对象
     * @return 返回解析后的 LambdaMeta
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 17:32
     */
    public static LambdaMeta realExtract(ISupplier<?> lambda) {
        if (!lambda.getClass().isSynthetic()) {
            throw ExceptionUtils.mpe("该方法仅能传入 lambda 表达式产生的合成类");
        }
        // 1. IDEA 调试模式下 lambda 表达式是一个代理
        if (lambda instanceof Proxy) {
            return new IdeaProxyLambdaMeta((Proxy) lambda);
        }
        // 2. 反射读取
        try {
            Method method = lambda.getClass().getDeclaredMethod("writeReplace");
            return new ReflectLambdaMeta((java.lang.invoke.SerializedLambda) ReflectionKit.setAccessible(method).invoke(lambda));
        } catch (Throwable e) {
            // 3. 反射失败使用序列化的方式读取
            return new ShadowLambdaMeta(SerializedLambda.extract(lambda));
        }
    }

    /**
     * 获取属性名
     *
     * @param lambda lambda对象
     * @return java.lang.String
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/4 0004 17:33
     */
    public static String getAttribute(ISupplier<?> lambda) {
        LambdaMeta extract = extract(lambda);
        return PropertyNamer.methodToProperty(extract.getImplMethodName());
    }

    public static <T> String getAttribute(SFunction<T, ?> lambda) {
        LambdaMeta extract = LambdaUtils.extract(lambda);
        return PropertyNamer.methodToProperty(extract.getImplMethodName());
    }

    public static <T> String getAttribute(SFunction<T, ?> lambda, Class<T> type) {
        LambdaMeta extract = LambdaUtils.extract(lambda);
        return PropertyNamer.methodToProperty(extract.getImplMethodName());
    }

    /**
     * 转换成map
     *
     * @param list        列表数据
     * @param keyFunction 转成的map的key
     * @param <ENTITY>    实体
     * @param <FIELD>     实体类的属性，作为key
     * @return java.util.Map<FIELD, ENTITY> 返回Map<属性,实体>
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/31 18:22
     */
    public static <ENTITY, FIELD> Map<FIELD, ENTITY> toMap(List<ENTITY> list, SFunction<ENTITY, FIELD> keyFunction) {
        if (list == null) {
            return Collections.emptyMap();
        }
        return list.parallelStream().collect(Collectors.toMap(keyFunction, Function.identity(), (l, r) -> r));
    }

    /**
     * 转换成map
     *
     * @param list          列表数据
     * @param keyFunction   转成的map的key
     * @param valueFunction 转成的map的value
     * @param <ENTITY>      实体
     * @param <FIELD_KEY>   实体类的属性，作为key
     * @param <FIELD_VALUE> 实体类的属性，作为value
     * @return java.util.Map<FIELD_KEY, FIELD_VALUE> 返回Map<key属性,value属性>
     * @author <achao1441470436@gmail.com>
     * @since 2021/5/31 18:23
     */
    public static <ENTITY, FIELD_KEY, FIELD_VALUE> Map<FIELD_KEY, FIELD_VALUE>
    toMap(List<ENTITY> list,
          SFunction<ENTITY, FIELD_KEY> keyFunction,
          SFunction<ENTITY, FIELD_VALUE> valueFunction) {
        if (list == null) {
            return Collections.emptyMap();
        }
        return list.parallelStream().collect(Collectors.toMap(keyFunction, valueFunction, (l, r) -> r));
    }


    /**
     * 解析 lambda 表达式, 该方法只是调用了 {@link #realExtract(ISupplier)} 中的方法，在此基础上加了缓存。
     * 该缓存可能会在任意不定的时间被清除
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型，被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     * @see #realExtract(ISupplier)
     */
    public static <T> LambdaMeta extract(ISupplier<T> func) {
        Class<?> clazz = func.getClass();
        String name = clazz.getName();
        return Optional.ofNullable(FUNC_CACHE.get(name))
                .map(WeakReference::get)
                .orElseGet(() -> {
                    LambdaMeta lambda = realExtract(func);
                    FUNC_CACHE.put(name, new WeakReference<>(lambda));
                    return lambda;
                });
    }


}
